package com.alibaba.druid.pool;

/**
 * @param
 * @author zhx
 * @description
 * @return
 * @create 2023/7/3
 */

import cn.hutool.core.collection.CollUtil;
import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.FilterChainImpl;
import com.alibaba.druid.pool.DruidPooledPreparedStatement.PreparedStatementKey;
import com.alibaba.druid.pool.PreparedStatementPool.MethodType;
import com.alibaba.druid.proxy.jdbc.TransactionInfo;
import com.alibaba.druid.support.logging.Log;
import com.alibaba.druid.support.logging.LogFactory;
import com.alibaba.druid.util.JdbcUtils;

import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.PooledConnection;
import javax.sql.StatementEventListener;
import java.sql.*;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.locks.ReentrantLock;

public class DruidPooledConnection extends PoolableWrapper implements PooledConnection, Connection {
    private static final Log LOG = LogFactory.getLog(DruidPooledConnection.class);
    public static final int MAX_RECORD_SQL_COUNT = 10;
    protected Connection conn;
    protected volatile DruidConnectionHolder holder;
    protected TransactionInfo transactionInfo;
    private final boolean dupCloseLogEnable;
    protected volatile boolean traceEnable;
    private volatile boolean disable;
    protected volatile boolean closed;
    static AtomicIntegerFieldUpdater closingUpdater = AtomicIntegerFieldUpdater.newUpdater(DruidPooledConnection.class, "closing");
    protected final Thread ownerThread;
    private long connectedTimeMillis;
    private long connectedTimeNano;
    private volatile boolean running;
    private volatile boolean abandoned;
    protected StackTraceElement[] connectStackTrace;
    protected Throwable disableError;
    final ReentrantLock lock;
    protected volatile int closing;
    private static final String DUP_CLOSE="dup close";

    public DruidPooledConnection(DruidConnectionHolder holder) {
        super(holder.getConnection());
        this.conn = holder.getConnection();
        this.holder = holder;
        this.lock = holder.lock;
        this.dupCloseLogEnable = holder.getDataSource().isDupCloseLogEnable();
        this.ownerThread = Thread.currentThread();
        this.connectedTimeMillis = System.currentTimeMillis();
    }

    public long getConnectedTimeMillis() {
        return this.connectedTimeMillis;
    }

    public Thread getOwnerThread() {
        return this.ownerThread;
    }

    public StackTraceElement[] getConnectStackTrace() {
        return this.connectStackTrace;
    }

    public void setConnectStackTrace(StackTraceElement[] connectStackTrace) {
        this.connectStackTrace = connectStackTrace;
    }

    public long getConnectedTimeNano() {
        return this.connectedTimeNano;
    }

    public void setConnectedTimeNano() {
        if (this.connectedTimeNano <= 0L) {
            this.setConnectedTimeNano(System.nanoTime());
        }

    }

    public void setConnectedTimeNano(long connectedTimeNano) {
        this.connectedTimeNano = connectedTimeNano;
    }

    public boolean isTraceEnable() {
        return this.traceEnable;
    }

    public void setTraceEnable(boolean traceEnable) {
        this.traceEnable = traceEnable;
    }

    public SQLException handleException(Throwable t) throws SQLException {
        return this.handleException(t, (String)null);
    }

    public SQLException handleException(Throwable t, String sql) throws SQLException {
        DruidConnectionHolder druidConnectionHolder = this.holder;
        if (druidConnectionHolder != null) {
            DruidAbstractDataSource dataSource = druidConnectionHolder.getDataSource();
            dataSource.handleConnectionException(this, t, sql);
        }

        if (t instanceof SQLException) {
            throw (SQLException)t;
        } else {
            throw new SQLException("Error", t);
        }
    }

    public boolean isOracle() {
        return this.holder.getDataSource().isOracle();
    }

    public void closePoolableStatement(DruidPooledPreparedStatement stmt) throws SQLException {
        PreparedStatement rawStatement = stmt.getRawPreparedStatement();
        DruidConnectionHolder druidConnectionHolder = this.holder;
        if (druidConnectionHolder != null) {
            if (stmt.isPooled()) {
                try {
                    rawStatement.clearParameters();
                } catch (SQLException var14) {
                    this.handleException(var14, (String)null);
                    if (rawStatement.getConnection().isClosed()) {
                        return;
                    }

                    LOG.error("clear parameter error", var14);
                }

                try {
                    rawStatement.clearBatch();
                } catch (SQLException var13) {
                    this.handleException(var13, (String)null);
                    if (rawStatement.getConnection().isClosed()) {
                        return;
                    }

                    LOG.error("clear batch error", var13);
                }
            }

            PreparedStatementHolder stmtHolder = stmt.getPreparedStatementHolder();
            stmtHolder.decrementInUseCount();
            if (stmt.isPooled() && druidConnectionHolder.isPoolPreparedStatements() && stmt.exceptionCount == 0) {
                druidConnectionHolder.getStatementPool().put(stmtHolder);
                stmt.clearResultSet();
                druidConnectionHolder.removeTrace(stmt);
                stmtHolder.setFetchRowPeak(stmt.getFetchRowPeak());
                stmt.setClosed(true);
            } else if (stmt.isPooled() && druidConnectionHolder.isPoolPreparedStatements()) {
                stmt.clearResultSet();
                druidConnectionHolder.removeTrace(stmt);
                druidConnectionHolder.getStatementPool().remove(stmtHolder);
            } else {
                try {
                    stmt.closeInternal();
                } catch (SQLException var11) {
                    this.handleException(var11, (String)null);
                    throw var11;
                } finally {
                    druidConnectionHolder.getDataSource().incrementClosedPreparedStatementCount();
                }
            }

        }
    }

    public DruidConnectionHolder getConnectionHolder() {
        return this.holder;
    }

    public Connection getConnection() {
        if (!this.holder.underlyingAutoCommit) {
            this.createTransactionInfo();
        }

        return this.conn;
    }

    public void disable() {
        this.disable((Throwable)null);
    }

    public void disable(Throwable error) {
        if (this.holder != null) {
            this.holder.clearStatementCache();
        }

        this.traceEnable = false;
        this.holder = null;
        this.transactionInfo = null;
        this.disable = true;
        this.disableError = error;
    }

    public boolean isDisable() {
        return this.disable;
    }

    public void close() throws SQLException {
        if (!this.disable) {
            DruidConnectionHolder druidConnectionHolder = this.holder;
            if (druidConnectionHolder == null) {
                if (this.dupCloseLogEnable) {
                    LOG.error(DUP_CLOSE);
                }

            } else {
                DruidAbstractDataSource dataSource = druidConnectionHolder.getDataSource();
                boolean isSameThread = this.getOwnerThread() == Thread.currentThread();
                if (!isSameThread) {
                    dataSource.setAsyncCloseConnectionEnable(true);
                }

                if (dataSource.isAsyncCloseConnectionEnable()) {
                    this.syncClose();
                } else if (closingUpdater.compareAndSet(this, 0, 1)) {
                    try {
                        Iterator var4 = druidConnectionHolder.getConnectionEventListeners().iterator();

                        while(true) {
                            if (!var4.hasNext()) {
                                List<Filter> filters = dataSource.getProxyFilters();
                                if (CollUtil.isNotEmpty(filters)) {
                                    FilterChainImpl filterChain = new FilterChainImpl(dataSource);
                                    filterChain.dataSource_recycle(this);
                                } else {
                                    this.recycle();
                                }
                                break;
                            }

                            ConnectionEventListener listener = (ConnectionEventListener)var4.next();
                            listener.connectionClosed(new ConnectionEvent(this));
                        }
                    } finally {
                        closingUpdater.set(this, 0);
                    }

                    this.disable = true;
                }
            }
        }
    }

    public void syncClose() throws SQLException {
        this.lock.lock();

        try {
            if (!this.disable && closingUpdater.get(this) == 0) {
                DruidConnectionHolder druidConnectionHolder = this.holder;
                if (druidConnectionHolder == null) {
                    if (this.dupCloseLogEnable) {
                        LOG.error(DUP_CLOSE);
                    }

                    return;
                }

                if (!closingUpdater.compareAndSet(this, 0, 1)) {
                    return;
                }

                Iterator var2 = druidConnectionHolder.getConnectionEventListeners().iterator();

                while(var2.hasNext()) {
                    ConnectionEventListener listener = (ConnectionEventListener)var2.next();
                    listener.connectionClosed(new ConnectionEvent(this));
                }

                DruidAbstractDataSource dataSource = druidConnectionHolder.getDataSource();
                List<Filter> filters = dataSource.getProxyFilters();
                if (CollUtil.isNotEmpty(filters)) {
                    FilterChainImpl filterChain = new FilterChainImpl(dataSource);
                    filterChain.dataSource_recycle(this);
                } else {
                    this.recycle();
                }

                this.disable = true;

            }
        } finally {
            closingUpdater.set(this, 0);
            this.lock.unlock();
        }

    }

    public void recycle() throws SQLException {
        if (!this.disable) {
            DruidConnectionHolder druidConnectionHolder = this.holder;
            if (druidConnectionHolder == null) {
                if (this.dupCloseLogEnable) {
                    LOG.error(DUP_CLOSE);
                }

            } else {
                if (!this.abandoned) {
                    DruidAbstractDataSource dataSource = druidConnectionHolder.getDataSource();
                    dataSource.recycle(this);
                }

                this.holder = null;
                this.conn = null;
                this.transactionInfo = null;
                this.closed = true;
            }
        }
    }

    public PreparedStatement prepareStatement(String sql) throws SQLException {
        this.checkState();
        PreparedStatementHolder stmtHolder = null;
        PreparedStatementKey key = new PreparedStatementKey(sql, this.getCatalog(), MethodType.M1);
        boolean poolPreparedStatements = this.holder.isPoolPreparedStatements();
        if (poolPreparedStatements) {
            stmtHolder = this.holder.getStatementPool().get(key);
        }

        if (stmtHolder == null) {
            try {
                stmtHolder = new PreparedStatementHolder(key, this.conn.prepareStatement(sql));
                this.holder.getDataSource().incrementPreparedStatementCount();
            } catch (SQLException var6) {
                this.handleException(var6, sql);
            }
        }

        this.initStatement(stmtHolder);
        DruidPooledPreparedStatement rtnVal = new DruidPooledPreparedStatement(this, stmtHolder);
        this.holder.addTrace(rtnVal);
        return rtnVal;
    }

    private void initStatement(PreparedStatementHolder stmtHolder) throws SQLException {
        if(Objects.nonNull(stmtHolder)){
            stmtHolder.incrementInUseCount();
            this.holder.getDataSource().initStatement(this, stmtHolder.statement);
        }
    }

    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        this.checkState();
        PreparedStatementHolder stmtHolder = null;
        PreparedStatementKey key = new PreparedStatementKey(sql, this.getCatalog(), MethodType.M2, resultSetType, resultSetConcurrency);
        boolean poolPreparedStatements = this.holder.isPoolPreparedStatements();
        if (poolPreparedStatements) {
            stmtHolder = this.holder.getStatementPool().get(key);
        }

        if (stmtHolder == null) {
            try {
                stmtHolder = new PreparedStatementHolder(key, this.conn.prepareStatement(sql, resultSetType, resultSetConcurrency));
                this.holder.getDataSource().incrementPreparedStatementCount();
            } catch (SQLException var8) {
                this.handleException(var8, sql);
            }
        }

        this.initStatement(stmtHolder);
        DruidPooledPreparedStatement rtnVal = new DruidPooledPreparedStatement(this, stmtHolder);
        this.holder.addTrace(rtnVal);
        return rtnVal;
    }

    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        this.checkState();
        PreparedStatementHolder stmtHolder = null;
        PreparedStatementKey key = new PreparedStatementKey(sql, this.getCatalog(), MethodType.M3, resultSetType, resultSetConcurrency, resultSetHoldability);
        boolean poolPreparedStatements = this.holder.isPoolPreparedStatements();
        if (poolPreparedStatements) {
            stmtHolder = this.holder.getStatementPool().get(key);
        }

        if (stmtHolder == null) {
            try {
                stmtHolder = new PreparedStatementHolder(key, this.conn.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
                this.holder.getDataSource().incrementPreparedStatementCount();
            } catch (SQLException var9) {
                this.handleException(var9, sql);
            }
        }

        this.initStatement(stmtHolder);
        DruidPooledPreparedStatement rtnVal = new DruidPooledPreparedStatement(this, stmtHolder);
        this.holder.addTrace(rtnVal);
        return rtnVal;
    }

    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
        this.checkState();
        PreparedStatementKey key = new PreparedStatementKey(sql, this.getCatalog(), MethodType.M4, columnIndexes);
        PreparedStatementHolder stmtHolder = null;
        boolean poolPreparedStatements = this.holder.isPoolPreparedStatements();
        if (poolPreparedStatements) {
            stmtHolder = this.holder.getStatementPool().get(key);
        }

        if (stmtHolder == null) {
            try {
                stmtHolder = new PreparedStatementHolder(key, this.conn.prepareStatement(sql, columnIndexes));
                this.holder.getDataSource().incrementPreparedStatementCount();
            } catch (SQLException var7) {
                this.handleException(var7, sql);
            }
        }

        this.initStatement(stmtHolder);
        DruidPooledPreparedStatement rtnVal = new DruidPooledPreparedStatement(this, stmtHolder);
        this.holder.addTrace(rtnVal);
        return rtnVal;
    }

    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
        this.checkState();
        PreparedStatementKey key = new PreparedStatementKey(sql, this.getCatalog(), MethodType.M5, columnNames);
        PreparedStatementHolder stmtHolder = null;
        boolean poolPreparedStatements = this.holder.isPoolPreparedStatements();
        if (poolPreparedStatements) {
            stmtHolder = this.holder.getStatementPool().get(key);
        }

        if (stmtHolder == null) {
            try {
                stmtHolder = new PreparedStatementHolder(key, this.conn.prepareStatement(sql, columnNames));
                this.holder.getDataSource().incrementPreparedStatementCount();
            } catch (SQLException var7) {
                this.handleException(var7, sql);
            }
        }

        this.initStatement(stmtHolder);
        DruidPooledPreparedStatement rtnVal = new DruidPooledPreparedStatement(this, stmtHolder);
        this.holder.addTrace(rtnVal);
        return rtnVal;
    }

    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
        this.checkState();
        PreparedStatementKey key = new PreparedStatementKey(sql, this.getCatalog(), MethodType.M6, autoGeneratedKeys);
        PreparedStatementHolder stmtHolder = null;
        boolean poolPreparedStatements = this.holder.isPoolPreparedStatements();
        if (poolPreparedStatements) {
            stmtHolder = this.holder.getStatementPool().get(key);
        }

        if (stmtHolder == null) {
            try {
                stmtHolder = new PreparedStatementHolder(key, this.conn.prepareStatement(sql, autoGeneratedKeys));
                this.holder.getDataSource().incrementPreparedStatementCount();
            } catch (SQLException var7) {
                this.handleException(var7, sql);
            }
        }

        this.initStatement(stmtHolder);
        DruidPooledPreparedStatement rtnVal = new DruidPooledPreparedStatement(this, stmtHolder);
        this.holder.addTrace(rtnVal);
        return rtnVal;
    }

    public CallableStatement prepareCall(String sql) throws SQLException {
        this.checkState();
        PreparedStatementHolder stmtHolder = null;
        PreparedStatementKey key = new PreparedStatementKey(sql, this.getCatalog(), MethodType.Precall_1);
        boolean poolPreparedStatements = this.holder.isPoolPreparedStatements();
        if (poolPreparedStatements) {
            stmtHolder = this.holder.getStatementPool().get(key);
        }

        if (stmtHolder == null) {
            try {
                stmtHolder = new PreparedStatementHolder(key, this.conn.prepareCall(sql));
                this.holder.getDataSource().incrementPreparedStatementCount();
            } catch (SQLException var6) {
                this.handleException(var6, sql);
            }
        }

        this.initStatement(stmtHolder);
        DruidPooledCallableStatement rtnVal = new DruidPooledCallableStatement(this, stmtHolder);
        this.holder.addTrace(rtnVal);
        return rtnVal;
    }

    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        this.checkState();
        PreparedStatementHolder stmtHolder = null;
        PreparedStatementKey key = new PreparedStatementKey(sql, this.getCatalog(), MethodType.Precall_2, resultSetType, resultSetConcurrency, resultSetHoldability);
        boolean poolPreparedStatements = this.holder.isPoolPreparedStatements();
        if (poolPreparedStatements) {
            stmtHolder = this.holder.getStatementPool().get(key);
        }

        if (stmtHolder == null) {
            try {
                stmtHolder = new PreparedStatementHolder(key, this.conn.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability));
                this.holder.getDataSource().incrementPreparedStatementCount();
            } catch (SQLException var9) {
                this.handleException(var9, sql);
            }
        }

        this.initStatement(stmtHolder);
        DruidPooledCallableStatement rtnVal = new DruidPooledCallableStatement(this, stmtHolder);
        this.holder.addTrace(rtnVal);
        return rtnVal;
    }

    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        this.checkState();
        PreparedStatementHolder stmtHolder = null;
        PreparedStatementKey key = new PreparedStatementKey(sql, this.getCatalog(), MethodType.Precall_3, resultSetType, resultSetConcurrency);
        boolean poolPreparedStatements = this.holder.isPoolPreparedStatements();
        if (poolPreparedStatements) {
            stmtHolder = this.holder.getStatementPool().get(key);
        }

        if (stmtHolder == null) {
            try {
                stmtHolder = new PreparedStatementHolder(key, this.conn.prepareCall(sql, resultSetType, resultSetConcurrency));
                this.holder.getDataSource().incrementPreparedStatementCount();
            } catch (SQLException var8) {
                this.handleException(var8, sql);
            }
        }

        this.initStatement(stmtHolder);
        DruidPooledCallableStatement rtnVal = new DruidPooledCallableStatement(this, stmtHolder);
        this.holder.addTrace(rtnVal);
        return rtnVal;
    }

    public Statement createStatement() throws SQLException {
        this.checkState();
        Statement stmt = null;

        try {
            stmt = this.conn.createStatement();
        } catch (SQLException var3) {
            this.handleException(var3, (String)null);
        }

        this.holder.getDataSource().initStatement(this, stmt);
        DruidPooledStatement poolableStatement = new DruidPooledStatement(this, stmt);
        this.holder.addTrace(poolableStatement);
        return poolableStatement;
    }

    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        this.checkState();
        Statement stmt = null;

        try {
            stmt = this.conn.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
        } catch (SQLException var6) {
            this.handleException(var6, (String)null);
        }

        this.holder.getDataSource().initStatement(this, stmt);
        DruidPooledStatement poolableStatement = new DruidPooledStatement(this, stmt);
        this.holder.addTrace(poolableStatement);
        return poolableStatement;
    }

    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        this.checkState();
        Statement stmt = null;

        try {
            stmt = this.conn.createStatement(resultSetType, resultSetConcurrency);
        } catch (SQLException var5) {
            this.handleException(var5, (String)null);
        }

        this.holder.getDataSource().initStatement(this, stmt);
        DruidPooledStatement poolableStatement = new DruidPooledStatement(this, stmt);
        this.holder.addTrace(poolableStatement);
        return poolableStatement;
    }

    public String nativeSQL(String sql) throws SQLException {
        this.checkState();
        return this.conn.nativeSQL(sql);
    }

    public void setAutoCommit(boolean autoCommit) throws SQLException {
        this.checkState();
        boolean useLocalSessionState = this.holder.getDataSource().isUseLocalSessionState();
        if (!useLocalSessionState || autoCommit != this.holder.underlyingAutoCommit) {
            try {
                this.conn.setAutoCommit(autoCommit);
                this.holder.setUnderlyingAutoCommit(autoCommit);
                this.holder.setLastExecTimeMillis(System.currentTimeMillis());
            } catch (SQLException var4) {
                this.handleException(var4, (String)null);
            }

        }
    }

    protected void transactionRecord(String sql) throws SQLException {
        if (this.holder != null) {
            this.holder.setLastExecTimeMillis(System.currentTimeMillis());
        }

        if (this.transactionInfo == null && !this.conn.getAutoCommit()) {
            DruidAbstractDataSource dataSource = this.holder.getDataSource();
            dataSource.incrementStartTransactionCount();
            this.transactionInfo = new TransactionInfo(dataSource.createTransactionId());
        }

        if (this.transactionInfo != null) {
            List<String> sqlList = this.transactionInfo.getSqlList();
            if (sqlList.size() < 10) {
                sqlList.add(sql);
            }
        }

    }

    public boolean getAutoCommit() throws SQLException {
        this.checkState();
        return this.conn.getAutoCommit();
    }

    public void commit() throws SQLException {
        this.checkState();
        DruidAbstractDataSource dataSource = this.holder.getDataSource();
        dataSource.incrementCommitCount();

        try {
            this.conn.commit();
        } catch (SQLException var6) {
            this.handleException(var6, (String)null);
        } finally {
            this.handleEndTransaction(dataSource, (Savepoint)null);
        }

    }

    public TransactionInfo getTransactionInfo() {
        return this.transactionInfo;
    }

    protected void createTransactionInfo() {
        DruidAbstractDataSource dataSource = this.holder.getDataSource();
        dataSource.incrementStartTransactionCount();
        this.transactionInfo = new TransactionInfo(dataSource.createTransactionId());
    }

    public void rollback() throws SQLException {
        if (this.transactionInfo != null&&this.holder != null) {

                DruidAbstractDataSource dataSource = this.holder.getDataSource();
                dataSource.incrementRollbackCount();

                try {
                    this.conn.rollback();
                } catch (SQLException var6) {
                    this.handleException(var6, (String)null);
                } finally {
                    this.handleEndTransaction(dataSource, (Savepoint)null);
                }

            }
        }


    public Savepoint setSavepoint(String name) throws SQLException {
        this.checkState();

        try {
            return this.conn.setSavepoint(name);
        } catch (SQLException var3) {
            this.handleException(var3, (String)null);
            return null;
        }
    }

    public void rollback(Savepoint savepoint) throws SQLException {
        if (this.holder != null) {
            DruidAbstractDataSource dataSource = this.holder.getDataSource();
            dataSource.incrementRollbackCount();

            try {
                this.conn.rollback(savepoint);
            } catch (SQLException var7) {
                this.handleException(var7, (String)null);
            } finally {
                this.handleEndTransaction(dataSource, savepoint);
            }

        }
    }

    private void handleEndTransaction(DruidAbstractDataSource dataSource, Savepoint savepoint) {
        if (this.transactionInfo != null && savepoint == null) {
            this.transactionInfo.setEndTimeMillis();
            long transactionMillis = this.transactionInfo.getEndTimeMillis() - this.transactionInfo.getStartTimeMillis();
            dataSource.getTransactionHistogram().record(transactionMillis);
            dataSource.logTransaction(this.transactionInfo);
            this.transactionInfo = null;
        }

    }

    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
        this.checkState();

        try {
            this.conn.releaseSavepoint(savepoint);
        } catch (SQLException var3) {
            this.handleException(var3, (String)null);
        }

    }

    public Clob createClob() throws SQLException {
        this.checkState();

        try {
            return this.conn.createClob();
        } catch (SQLException var2) {
            this.handleException(var2, (String)null);
            return null;
        }
    }

    public boolean isClosed() throws SQLException {
        if (this.holder == null) {
            return true;
        } else {
            return this.closed || this.disable;
        }
    }

    public boolean isAbandonded() {
        return this.abandoned;
    }

    public DatabaseMetaData getMetaData() throws SQLException {
        this.checkState();
        if (!this.holder.underlyingAutoCommit) {
            this.createTransactionInfo();
        }

        return this.conn.getMetaData();
    }

    public void setReadOnly(boolean readOnly) throws SQLException {
        this.checkState();
        boolean useLocalSessionState = this.holder.getDataSource().isUseLocalSessionState();
        if (!useLocalSessionState || readOnly != this.holder.isUnderlyingReadOnly()) {
            try {
                this.conn.setReadOnly(readOnly);
            } catch (SQLException var4) {
                this.handleException(var4, (String)null);
            }

            this.holder.setUnderlyingReadOnly(readOnly);
        }
    }

    public boolean isReadOnly() throws SQLException {
        this.checkState();
        return this.conn.isReadOnly();
    }

    public void setCatalog(String catalog) throws SQLException {
        this.checkState();

        try {
            this.conn.setCatalog(catalog);
        } catch (SQLException var3) {
            this.handleException(var3, (String)null);
        }

    }

    public String getCatalog() throws SQLException {
        this.checkState();
        return this.conn.getCatalog();
    }

    public void setTransactionIsolation(int level) throws SQLException {
        this.checkState();
        boolean useLocalSessionState = this.holder.getDataSource().isUseLocalSessionState();
        if (!useLocalSessionState || level != this.holder.getUnderlyingTransactionIsolation()) {
            try {
                this.conn.setTransactionIsolation(level);
            } catch (SQLException var4) {
                this.handleException(var4, (String)null);
            }

            this.holder.setUnderlyingTransactionIsolation(level);
        }
    }

    public int getTransactionIsolation() throws SQLException {
        this.checkState();
        return this.holder.getUnderlyingTransactionIsolation();
    }

    public SQLWarning getWarnings() throws SQLException {
        this.checkState();
        return this.conn.getWarnings();
    }

    public void clearWarnings() throws SQLException {
        this.checkState();

        try {
            this.conn.clearWarnings();
        } catch (SQLException var2) {
            this.handleException(var2, (String)null);
        }

    }

    public Map<String, Class<?>> getTypeMap() throws SQLException {
        this.checkState();
        return this.conn.getTypeMap();
    }

    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
        this.checkState();
        this.conn.setTypeMap(map);
    }

    public void setHoldability(int holdability) throws SQLException {
        this.checkState();
        boolean useLocalSessionState = this.holder.getDataSource().isUseLocalSessionState();
        if (!useLocalSessionState || holdability != this.holder.getUnderlyingHoldability()) {
            this.conn.setHoldability(holdability);
            this.holder.setUnderlyingHoldability(holdability);
        }
    }

    public int getHoldability() throws SQLException {
        this.checkState();
        if (!this.holder.underlyingAutoCommit) {
            this.createTransactionInfo();
        }

        return this.conn.getHoldability();
    }

    public Savepoint setSavepoint() throws SQLException {
        this.checkState();

        try {
            return this.conn.setSavepoint();
        } catch (SQLException var2) {
            this.handleException(var2, (String)null);
            return null;
        }
    }

    public Blob createBlob() throws SQLException {
        this.checkState();
        return this.conn.createBlob();
    }

    public NClob createNClob() throws SQLException {
        this.checkState();
        return this.conn.createNClob();
    }

    public SQLXML createSQLXML() throws SQLException {
        this.checkState();
        return this.conn.createSQLXML();
    }

    public boolean isValid(int timeout) throws SQLException {
        this.checkState();
        return this.conn.isValid(timeout);
    }

    public void setClientInfo(String name, String value) throws SQLClientInfoException {
        if (this.holder == null) {
            throw new SQLClientInfoException();
        } else {
            this.conn.setClientInfo(name, value);
        }
    }

    public void setClientInfo(Properties properties) throws SQLClientInfoException {
        if (this.holder == null) {
            throw new SQLClientInfoException();
        } else {
            this.conn.setClientInfo(properties);
        }
    }

    public String getClientInfo(String name) throws SQLException {
        this.checkState();
        return this.conn.getClientInfo(name);
    }

    public Properties getClientInfo() throws SQLException {
        this.checkState();
        return this.conn.getClientInfo();
    }

    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
        this.checkState();
        return this.conn.createArrayOf(typeName, elements);
    }

    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
        this.checkState();
        return this.conn.createStruct(typeName, attributes);
    }

    public void addConnectionEventListener(ConnectionEventListener listener) {
        if (this.holder == null) {
            throw new IllegalStateException();
        } else {
            this.holder.getConnectionEventListeners().add(listener);
        }
    }

    public void removeConnectionEventListener(ConnectionEventListener listener) {
        if (this.holder == null) {
            throw new IllegalStateException();
        } else {
            this.holder.getConnectionEventListeners().remove(listener);
        }
    }

    public void addStatementEventListener(StatementEventListener listener) {
        if (this.holder == null) {
            throw new IllegalStateException();
        } else {
            this.holder.getStatementEventListeners().add(listener);
        }
    }

    public void removeStatementEventListener(StatementEventListener listener) {
        if (this.holder == null) {
            throw new IllegalStateException();
        } else {
            this.holder.getStatementEventListeners().remove(listener);
        }
    }

    public Throwable getDisableError() {
        return this.disableError;
    }

    public void checkState() throws SQLException {
        boolean asyncCloseEnabled;
        if (this.holder != null) {
            asyncCloseEnabled = this.holder.getDataSource().isAsyncCloseConnectionEnable();
        } else {
            asyncCloseEnabled = false;
        }

        if (asyncCloseEnabled) {
            this.lock.lock();

            try {
                this.checkStateInternal();
            } finally {
                this.lock.unlock();
            }
        } else {
            this.checkStateInternal();
        }

    }

    private void checkStateInternal() throws SQLException {
        if (this.closed) {
            if (this.disableError != null) {
                throw new SQLException("connection closed", this.disableError);
            } else {
                throw new SQLException("connection closed");
            }
        } else if (this.disable) {
            if (this.disableError != null) {
                throw new SQLException("connection disabled", this.disableError);
            } else {
                throw new SQLException("connection disabled");
            }
        } else if (this.holder == null) {
            if (this.disableError != null) {
                throw new SQLException("connection holder is null", this.disableError);
            } else {
                throw new SQLException("connection holder is null");
            }
        }
    }

    public String toString() {
        return this.conn != null ? this.conn.toString() : "closed-conn-" + System.identityHashCode(this);
    }

    public void setSchema(String schema) throws SQLException {
        String dbType = holder.dataSource.getDbType();
        boolean hasSchema = JdbcUtils.isMysqlDbType(dbType)
                || JdbcUtils.isPgsqlDbType(dbType)
                || JdbcUtils.isOracleDbType(dbType)
                || JdbcUtils.isSqlserverDbType(dbType)
                || JdbcUtils.isDmDbType(dbType);
        if (hasSchema) {
            conn.setSchema(schema);
            if (holder.statementPool != null) {
                holder.clearStatementCache();
            }
        } else {
            throw new SQLFeatureNotSupportedException();
        }
    }


    public String getSchema() throws SQLException {
        return this.conn.getSchema();
    }

    public void abort(Executor executor) throws SQLException {
        this.conn.abort(executor);
    }

    public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
        this.conn.setNetworkTimeout(executor, milliseconds);
    }

    public int getNetworkTimeout() throws SQLException {
        return this.conn.getNetworkTimeout();
    }

    final void beforeExecute() {
        DruidConnectionHolder druidConnectionHolder = this.holder;
        if (druidConnectionHolder != null && druidConnectionHolder.dataSource.removeAbandoned) {
            this.running = true;
        }

    }

    final void afterExecute() {
        DruidConnectionHolder druidConnectionHolder = this.holder;
        if (druidConnectionHolder != null) {
            DruidAbstractDataSource dataSource = druidConnectionHolder.dataSource;
            if (dataSource.removeAbandoned) {
                this.running = false;
                druidConnectionHolder.lastActiveTimeMillis = System.currentTimeMillis();
            }

            dataSource.onFatalError = false;
        }

    }

    boolean isRunning() {
        return this.running;
    }

    public void abandond() {
        this.abandoned = true;
    }

    public long getPhysicalConnectNanoSpan() {
        return this.holder.getCreateNanoSpan();
    }

    public long getPhysicalConnectionUsedCount() {
        return this.holder.getUseCount();
    }

    public long getConnectNotEmptyWaitNanos() {
        return this.holder.getLastNotEmptyWaitNanos();
    }

    public Map<String, Object> getVariables() {
        return this.holder.variables;
    }

    public Map<String, Object> getGloablVariables() {
        return this.holder.globleVariables;
    }
}

